package facade;

import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import simulation.TrafficInfrastructureManager;
import domain.Car;
import domain.CrossRoad;
import domain.Road;
import domain.Segment;

public class TrafficInfrastructureFacade  {
	private final TrafficInfrastructureManager infrastructure;
	
	public TrafficInfrastructureFacade(TrafficInfrastructureManager infrastructure) {
		this.infrastructure = infrastructure;
		infrastructure.subscribeTrafficChangeObserver(
			new TrafficInfrastructureManager.TrafficChangeObserver() {
				public void notifyTrafficChange() {
					notifyObservers();
				}
			}
		);
	}
	
	/* crossroads */
	public Collection<CrossRoadInfo> getAllCrossRoads() {
		Collection<CrossRoad> crossRoads = infrastructure.getAllCrossRoads();
		ArrayList<CrossRoadInfo> lst = new ArrayList<CrossRoadInfo>();
		for (CrossRoad cr : crossRoads) {
			CrossRoadInfo info = new CrossRoadInfo();
			info.position = getCrossRoadCoordinate(cr);
			info.car = createCarInfo(cr.getSegment().getCar());
			info.id = cr.getId();
			lst.add(info);
		}
		return lst;
	}
	
	private Point2D getCrossRoadCoordinate(CrossRoad cr) {
		Segment s = cr.getSegment();
		return new Point2D.Double(s.getX(),s.getY());
	}
	
	/* cars */
	private CarInfo createCarInfo(Car car) {
		if(car == null) return null;
		CarInfo cinfo = new CarInfo();
		cinfo.label = car.getId();
		cinfo.position = new Point2D.Double(car.getPosition().getX(),car.getPosition().getY());
		return cinfo;
	}
	
	/*roads*/
	private Line2D getRoadLine(Road r) {
		Point2D start = getCrossRoadCoordinate(r.getStart());
		Point2D end = getCrossRoadCoordinate(r.getEnd());
		double diffX = Math.cos(r.getDirection())*0.5;
		double diffY = Math.sin(r.getDirection())*0.5;
		start.setLocation(start.getX()+diffX, start.getY()+diffY);
		end.setLocation(end.getX()-diffX, end.getY()-diffY);
		return new Line2D.Double(start,end);
	}
	
	public Collection<RoadInfo> getAllRoads() {
		Collection<Road> roads = infrastructure.getAllRoads();
		ArrayList<RoadInfo> lst = new ArrayList<RoadInfo>();
		for (Road r : roads) {
			RoadInfo info = new RoadInfo();
			info.position = getRoadLine(r);
			info.length = r.getLength();
			info.id = r.getId();
			info.cars = new ArrayList<CarInfo>();
			List<Segment> segs = r.getSegments();
			for(Segment s : segs) {
				info.cars.add(createCarInfo(s.getCar()));
			}
			lst.add(info);
		}
		return lst;
	}
	

	/*change notification */
	public interface TrafficChangeObserver {
		void notifyTrafficChange();
	}
	
	public void subscribeChangeObserver(TrafficChangeObserver obs) {
		observers.add(obs);
	}
	public void unsubscribeChangeObserver(TrafficChangeObserver obs) {
		observers.remove(obs);
	}
		
	private void notifyObservers() {
		for(TrafficChangeObserver obs : observers) {
			obs.notifyTrafficChange();
		}
	}
	private Set<TrafficChangeObserver> observers = new HashSet<TrafficChangeObserver>();

}
